# -*- coding: utf-8 -*-
import subprocess
import time
import logging
import argparse

from appium import webdriver
# from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
logger = logging

APP = "XHS"

app_package_map = {
    "XHS": ("com.xingin.xhs", ".index.v2.IndexActivityV2")
}


class XHSAppiumOperate:
    def __init__(self, **kwargs):
        self.screen_width = None
        self.screen_height = None

        self.deviceName = kwargs.get("deviceName")
        self.appium_url = kwargs.get("appium_url")
        self.platformVersion = kwargs.get("platformVersion")
        self.systemPort = kwargs.get("systemPort")
        self.adbPort = kwargs.get("adbPort")
        self.hide_recent_n_posts = kwargs.get("hide_recent_n_posts")

        self.driver = None
        self.max_retries = 3
        self.failure_count = 0
        self.view_count = 0
        self.like_count = 0
        self.comment_count = 0
        self.follow_count = 0

    def run(self):
        while self.max_retries > 0:
            try:
                if self.driver:
                    self.driver.quit()
                self.connection()
                logger.info(f"设备 {self.deviceName} 连接成功")
                time.sleep(3)

                logger.info("开始执行小红书【设置作品权限为仅自己可见】任务...")
                self.script()
                break
            except Exception as e:
                logger.error(f"发生异常: {e}")
                self.failure_count += 1
                self.max_retries -= 1
                if self.max_retries == 0:
                    self._exit_post()
                    raise
        logger.info("任务结束")

    @staticmethod
    def execute_adb_command(adb_command):
        """
        执行单条 ADB 命令

        :param adb_command: 完整的 ADB 命令字符串
        :return: 命令执行结果（stdout）
        """
        try:
            logger.info(f"执行 ADB 命令: {adb_command}")
            result = subprocess.run(
                adb_command,
                shell=True,
                check=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE
            )
            logger.info(f"命令执行成功: {adb_command}")
            return result.stdout.decode('utf-8')
        except subprocess.CalledProcessError as e:
            logger.error(f"命令执行失败: {adb_command}")
            logger.error(f"错误信息: {e.stderr.decode('utf-8')}")

    def initialize_device_settings(self):
        """
        执行设备初始化所需的 ADB 命令
        """
        logger.info("开始初始化设备设置")

        commands = [
            f"adb -s {self.deviceName} -P {self.adbPort} shell settings put global adb_keep_usb_enabled 1",
            f"adb -s {self.deviceName} -P {self.adbPort} shell settings put global miui_optimization 0",
            f"adb -s {self.deviceName} -P {self.adbPort} shell settings put global tcp_keepalive 1",
            f"adb -s {self.deviceName} -P {self.adbPort} shell dumpsys battery unplug",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm enable io.appium.uiautomator2.server",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm enable io.appium.uiautomator2.server.test",
            f"adb -s {self.deviceName} -P {self.adbPort} shell settings put global background_process_limit 5",
            f"adb -s {self.deviceName} -P {self.adbPort} shell am set-standby-bucket io.appium.uiautomator2.server ACTIVE",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm trim-caches 500M",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm clear io.appium.uiautomator2.server",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm clear io.appium.uiautomator2.server.test"
        ]

        for cmd in commands:
            try:
                self.execute_adb_command(cmd)
            except Exception as e:
                logger.error(f"执行命令失败: {cmd}")
                logger.error(f"错误详情: {e}")

    def connection(self):
        self.initialize_device_settings()
        desired_caps = {
            "platformName": "Android",
            # "udid": "FMR0223A31045650",
            "udid": self.deviceName,
            "platformVersion": self.platformVersion,
            "deviceName": self.deviceName,
            "automationName": "UiAutomator2",
            "appPackage": app_package_map[APP][0],
            "appActivity": app_package_map[APP][1],
            "noReset": True,
            "fullReset": False,
            "systemPort": int(self.systemPort),
            "adbPort": int(self.adbPort),
            "appWaitForLaunch": True,
            "noSign": True,
            "autoLaunch": True,
            "newCommandTimeout": 180,
            "skipLogcatCapture": True,
            "disableWindowAnimation": True,
            "disableAndroidWatchers": True,
            "adbExecTimeout": 30000,
            "uiautomator2ServerInstallTimeout": 60000,
            "skipDeviceInitialization": True,
        }

        # self.driver = webdriver.Remote(self.appium_url, options=UiAutomator2Options().load_capabilities(desired_caps))
        self.driver = webdriver.Remote(self.appium_url, desired_caps)
        self.screen_width, self.screen_height = self.get_window_size()
        self.wait_time = 2

    def script(self):
        logger.info("开始执行【将作品权限设置为仅自己可见】任务")

        # Step 1: 打开主页
        self._enter_profile_page()

        # Step 2: 计算需要滑动的次数
        # 如果指定了要隐藏的最近作品数，则计算需要滑动的次数（每次滑动能处理4个作品）
        max_scroll = (self.hide_recent_n_posts + 3) // 4  # 向上取整
        logger.info(f"需要隐藏最近 {self.hide_recent_n_posts} 条作品，预计滑动 {max_scroll} 次")

        # Step 3: 遍历多屏作品
        for scroll_count in range(max_scroll):
            logger.info(f"开始处理第 {scroll_count + 1} 屏作品")
            try:
                posts = None
                retries = 3
                for _ in range(retries):
                    try:
                        posts = self.driver.find_elements(
                            AppiumBy.XPATH,
                            '//android.widget.FrameLayout[starts-with(@content-desc, "笔记,")] | //android.widget.FrameLayout[starts-with(@content-desc, "视频,")]'
                        )
                        if posts:  # 如果找到了作品，跳出重试
                            break
                    except StaleElementReferenceException:
                        logger.warning("获取作品列表失败，重新尝试获取...")
                        time.sleep(2)
                    except NoSuchElementException:
                        logger.warning("没有找到作品元素，跳过当前页面")
                        break
                if not posts:
                    logger.error("超过重试次数，未找到作品，跳过当前页面")
                    continue
                # posts = self.driver.find_elements(
                #     AppiumBy.XPATH,
                #     '//android.widget.FrameLayout[starts-with(@content-desc, "笔记,")] | //android.widget.FrameLayout[starts-with(@content-desc, "视频,")]'
                # )

                for post in posts:
                    try:
                        if post:
                            post.click()
                            time.sleep(2)

                            self._open_post_menu()
                            self.swipe_left_from_button_and_find_permission()
                            self._set_permission_private()
                            self._exit_post()
                        else:
                            logger.warning("当前作品为 None，跳过此作品")
                    except StaleElementReferenceException:
                        logger.warning("作品元素失效，跳过此作品")
                        continue

                    except Exception as e:
                        logger.warning(f"处理作品失败: {e}")
                        self._exit_post(force=True)
                        continue
            except Exception as e:
                logger.error(f"获取作品失败或其他异常: {e}")

            # 只有在不是最后一页时才滑动
            if scroll_count < max_scroll - 1:
                self.swipe_to_next()
                time.sleep(2)

        logger.info("所有作品权限设置完成")
        self.normal_end()

    def _enter_profile_page(self):
        """进入个人主页"""
        try:
            logger.info("进入个人主页...")
            profile_btn = self.find_element_with_retry(
                by=AppiumBy.XPATH,
                value="//android.view.ViewGroup[@content-desc='我']",
                timeout=5
            )
            profile_btn.click()
            time.sleep(2)
        except Exception as e:
            logger.error(f"进入个人主页失败: {e}")
            raise

    def _open_post_menu(self):
        """点击图文或视频帖右上角菜单按钮"""
        logger.info("尝试点击右上角菜单按钮...")
        menu_xpaths = [
            '//android.widget.ImageView[@resource-id="com.xingin.xhs:id/moreOperateIV"]',  # 图文帖子
            '//android.widget.ImageView[@content-desc="更多"]'  # 视频帖子
        ]

        for xpath in menu_xpaths:
            try:
                menu_button = self.driver.find_element(
                    by=AppiumBy.XPATH,
                    value=xpath
                )
                menu_button.click()
                logger.info(f"点击菜单按钮")
                time.sleep(1)
                return  # 成功点击后就退出函数
            except Exception as e:
                logger.debug(f"未找到菜单按钮: {xpath}")

        logger.warning("未能找到任何可点击的菜单按钮")
        raise Exception("打开菜单失败，可能不是详情页或页面结构已变化")

    # def swipe_left_from_button_and_find_permission(self, duration=300):
    #     try:
    #         # 定位“编辑”按钮
    #         element = self.find_element_with_retry(
    #             by=AppiumBy.XPATH,
    #             value='//android.widget.Button[@content-desc="编辑"]',
    #             timeout=5
    #         )
    #     except Exception as e:
    #         logger.error(f"找不到按钮: {e}")
    #         return False
    #
    #     # 获取起点终点坐标
    #     location = element.location
    #     size = element.size
    #     center_x = location['x'] + size['width'] / 2
    #     center_y = location['y'] + size['height'] / 2
    #     start_x = center_x
    #     start_y = center_y
    #     end_x = center_x - size['width'] * 4
    #     end_y = center_y
    #
    #     # 执行第一次滑动
    #     self.driver.swipe(start_x, start_y, end_x, end_y, duration)
    #     time.sleep(3)
    #
    #     # 尝试查找“权限设置”
    #     try:
    #         permission_btn = self.find_element_with_retry(
    #             by=AppiumBy.XPATH,
    #             value='//android.widget.Button[@content-desc="权限设置"]',
    #             timeout=2
    #         )
    #         permission_btn.click()
    #         logger.info("成功点击权限设置")
    #         return True
    #     except NoSuchElementException:
    #         logger.info("第一次滑动后未找到权限设置，尝试第二次滑动")
    #     except StaleElementReferenceException:
    #         logger.warning("第一次滑动后元素失效，尝试重新查找权限设置")
    #
    #     # 执行第二次滑动
    #     self.driver.swipe(start_x, start_y, end_x, end_y, duration)
    #     time.sleep(3)
    #
    #     # 再次查找“权限设置”
    #     retries = 3  # 尝试 3 次
    #     for i in range(retries):
    #         try:
    #             permission_btn = self.find_element_with_retry(
    #                 by=AppiumBy.XPATH,
    #                 value='//android.widget.Button[@content-desc="权限设置"]',
    #                 timeout=2
    #             )
    #             permission_btn.click()
    #             logger.info("成功点击权限设置")
    #             return True
    #         except NoSuchElementException:
    #             logger.warning(f"第 {i + 1} 次尝试未找到权限设置，继续重试...")
    #         except Exception as e:
    #             logger.error(f"重试过程中发生错误: {e}")
    #         time.sleep(1)
    #
    #         # 如果多次尝试后仍未找到
    #     logger.error("两次滑动后仍未找到权限设置，跳过该作品")
    #     return False

    def swipe_left_from_button_and_find_permission(self, duration=300):
        # 定位"编辑"按钮
        element = self.find_element_with_retry(
            by=AppiumBy.XPATH,
            value='//android.widget.Button[@content-desc="编辑"]',
            timeout=5
        )
        if element is None:
            logger.error("找不到'编辑'按钮")
            return False

        # 获取起点终点坐标
        location = element.location
        size = element.size
        center_x = location['x'] + size['width'] / 2
        center_y = location['y'] + size['height'] / 2
        start_x = center_x
        start_y = center_y
        end_x = max(0, center_x - size['width'] * 4)  # 防止坐标为负数
        end_y = center_y

        # 执行第一次滑动
        try:
            self.driver.swipe(start_x, start_y, end_x, end_y, duration)
        except Exception as e:
            logger.error(f"第一次滑动失败: {e}")
            return False
        time.sleep(3)

        # 尝试查找"权限设置"
        permission_btn = self.find_element_with_retry(
            by=AppiumBy.XPATH,
            value='//android.widget.Button[@content-desc="权限设置"]',
            timeout=2
        )

        if permission_btn is not None:
            try:
                permission_btn.click()
                logger.info("成功点击权限设置")
                return True
            except Exception as e:
                logger.error(f"点击权限设置失败: {e}")
                return False
        else:
            logger.info("第一次滑动后未找到权限设置，尝试第二次滑动")

        # 执行第二次滑动
        try:
            self.driver.swipe(start_x, start_y, end_x, end_y, duration)
        except Exception as e:
            logger.error(f"第二次滑动失败: {e}")
            return False
        time.sleep(3)

        # 再次查找"权限设置"
        retries = 3  # 尝试 3 次
        for i in range(retries):
            permission_btn = self.find_element_with_retry(
                by=AppiumBy.XPATH,
                value='//android.widget.Button[@content-desc="权限设置"]',
                timeout=2
            )
            if permission_btn is not None:
                try:
                    permission_btn.click()
                    logger.info("成功点击权限设置")
                    return True
                except Exception as e:
                    logger.error(f"点击权限设置失败: {e}")
                    return False
            else:
                logger.warning(f"第 {i + 1} 次尝试未找到权限设置，继续重试...")
            time.sleep(1)

        # 如果多次尝试后仍未找到
        logger.error("两次滑动后仍未找到权限设置，跳过该作品")
        return False

    def _set_permission_private(self):
        """选择'仅自己可见'"""
        try:
            logger.info("设置权限为'仅自己可见'...")
            private_btn = self.find_element_with_retry(
                AppiumBy.XPATH,
                "//*[contains(@text, '仅自己可见')]",
                timeout=5
            )
            if private_btn is None:
                logger.error("找不到'仅自己可见'按钮")
                raise Exception("找不到'仅自己可见'按钮")
            private_btn.click()
            time.sleep(1)
        except Exception as e:
            logger.warning(f"设置权限失败: {e}")
            raise

    def _exit_post(self, force=False):
        """退出帖子详情页（或强制返回主页）"""

        logger.info("退出当前帖子...")
        self.driver.press_keycode(4)  # Android物理返回键

    def swipe_to_next(self):
        # 向上滑动
        self.driver.swipe(
            self.screen_width / 2,
            self.screen_height * 0.8,
            self.screen_width / 2,
            self.screen_height * 0.2,
            200
        )

    def find_element_with_retry(self, by, value, timeout=5):
        max_retries = 3
        for attempt in range(max_retries + 1):
            try:
                element = WebDriverWait(self.driver, timeout).until(
                    EC.presence_of_element_located((by, value))
                )
                return element
            except Exception as e:
                if attempt < max_retries:
                    logger.error(f"获取元素失败，重试")
                    time.sleep(1)  # 等待1秒后重试
                else:
                    logger.error(f"获取元素失败，超过3次")
                    return None

    def get_window_size(self):
        size = self.driver.get_window_size()
        return size['width'], size['height']

    def normal_end(self):
        logger.info("返回首页并退出")
        self.driver.press_keycode(3)  # HOME 键
        self.driver.quit()


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-j', '--job_id', dest='job_id', type=str, default=None, required=True, help='target job_id')
    parser.add_argument('-u', '--appium_url', dest='appium_url', type=str, default=None, help='target appium_url')
    parser.add_argument('-d', '--deviceName', dest='deviceName', type=str, default=None, help='target deviceName')
    parser.add_argument('-s', '--systemPort', dest='systemPort', type=str, default=None, help='target systemPort')
    parser.add_argument('-a', '--adbPort', dest='adbPort', type=str, default=None, help='target adbPort')
    parser.add_argument('-p', '--platformName', dest='platformName', type=str, default=None, help='target platformName')
    parser.add_argument('-accountId', '--accountId', dest='accountId', type=str, default="000", help='target accountId')
    parser.add_argument('-ver', '--platformVersion', dest='platformVersion', type=str, default=None,
                        help='target platformVersion')
    parser.add_argument('-v', '--variables', dest='variables', type=str, default='{}', help='target variables')
    parser.add_argument('-n_posts', '--hide_recent_n_posts', dest='hide_recent_n_posts', type=int, default=5,
                        help='number of recent posts to hide')

    args = parser.parse_args()
    XHSAppiumOperate(**vars(args)).run()